import fs from 'fs'
import Koa from 'koa'
import Router from 'koa-router'
import Parser from './Parser.js'
import Builder from './Builder.js'
import dgram from 'dgram'
import bodyParser from 'koa-bodyparser'
import cors from '@koa/cors'

const app = new Koa();
const router = new Router();

const pack = {
    "id": '0x867f',
    "flags": {
      "response": 1,
      "opcode": 0x0000,
      "authoritative": 0,
      "truncated": 0,
      "recursion_desired": 1,
      "recursion_available": 1,
      "z": 0,
      "authentic_data": 0,
      "checking_disable": 0,
      "error_code": 0x0000,
    },
    "query_count": 1,
    "answer_count": 3,
    "authority_count": 5,
    "additional_information_count": 5,
    
    "querys": [{
      "name": "www.baidu.com",
      "type": "A",
      // class is a keyword of javascript so rename it to klass.
      "klass": "IN"
    }],
    "answers": [
      {
        "name": "www.baidu.com",
        "type": "CNAME",
        "klass": "IN",
        "time_to_live": 600,
        "data_length": 15,
        "canonical_name": "www.a.shifen.com"
      },
      {
        "name": "www.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 600,
        "data_length": 4,
        "address": "14.215.177.38"
      },
      {
        "name": "www.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 600,
        "data_length": 4,
        "address": "14.215.177.38"
      }
    ],
    "authoritative_nameservers": [
      {
        "name": "a.shifen.com",
        "type": "NS",
        "klass": "IN",
        "time_to_live": 205,
        "data_length": 6,
        "nameserver": "ns3.a.shifen.com"
      },
      {
        "name": "a.shifen.com",
        "type": "NS",
        "klass": "IN",
        "time_to_live": 205,
        "data_length": 6,
        "nameserver": "ns4.a.shifen.com"
      },
      {
        "name": "a.shifen.com",
        "type": "NS",
        "klass": "IN",
        "time_to_live": 205,
        "data_length": 6,
        "nameserver": "ns2.a.shifen.com"
      },
      {
        "name": "a.shifen.com",
        "type": "NS",
        "klass": "IN",
        "time_to_live": 205,
        "data_length": 6,
        "nameserver": "ns1.a.shifen.com"
      },
      {
        "name": "a.shifen.com",
        "type": "NS",
        "klass": "IN",
        "time_to_live": 205,
        "data_length": 6,
        "nameserver": "ns5.a.shifen.com"
      }
    ],
    "additional_records": [
      {
        "name": "ns1.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 466,
        "data_length": 4,
        "address": "61.135.165.224"
      },
      {
        "name": "ns2.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 497,
        "data_length": 4,
        "address": "220.181.33.32"
      },
      {
        "name": "ns3.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 465,
        "data_length": 4,
        "address": "112.80.255.253"
      },
      {
        "name": "ns4.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 138,
        "data_length": 4,
        "address": "14.215.177.229"
      },
      {
        "name": "ns5.a.shifen.com",
        "type": "A",
        "klass": "IN",
        "time_to_live": 353,
        "data_length": 4,
        "address": "180.76.76.95"
      }
    ]
  } // finish pack object

  app.use(cors());
  app.use(bodyParser());

  router.get('/default', async ctx => {
    ctx.response.body = pack;
  });


  router.get('/parse', async ctx => {
    const binData = fs.readFileSync('dns_query.binary');
    let result = new Parser(binData).parse();
    ctx.response.body = result;
  })

  function sendPacket(ip, query) {
    return new Promise((resolve, reject) => {
      let b = new Builder();
      let buf = b.from(query).build();
  
      let client = dgram.createSocket('udp4');
      client.send(buf, 53, ip, (err, bytes) => {
        if (err) {
          reject(err);
          client.close();
        }
      });

      let packs = [];
      client.on("message", (msg, rinfo) => {
        if (rinfo.address === ip && rinfo.port === 53) {
          packs.push(msg);
          client.close();
        }
      });

      let result;
      let recieved_buf;
      client.on("close", () => {
        recieved_buf = Buffer.concat(packs);
        let parser = new Parser(recieved_buf);
        result = parser.parse();
        packs = [];
        resolve(result);
      });
    });
  }

  
  router.get('/query', async ctx => {
    let { gateway, name } = ctx.query;
    console.log(`gateway: ${gateway}, name: ${name}`);
    let result = await sendPacket(gateway, {qname: name});
    ctx.response.body = JSON.stringify(result, null, 2);
  })
  
  router.post('/send', async ctx => {
    let body = ctx.request.body;
    console.log(`body: ${JSON.stringify(body)}`);
    let { ip: gateway, packet } = body;
    let result = await sendPacket(gateway, packet);
    ctx.response.body = JSON.stringify(result, null, 2);
  })

  
  app.use(router.routes());

  app.listen(3000, () => {
    console.log("Server is running at http://localhost:3000");
  });
